﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Reflection;

namespace ELearning.Common.Converter
{
    public sealed class ModelConverter<T> where T : new()
    {
        public static List<T> ConvertToModel(DataTable dt)
        {
            List<T> entityList = new List<T>();
            Type type = typeof(T);
            foreach (DataRow dr in dt.Rows)
            {
                T entity = new T();
                PropertyInfo[] propertys = entity.GetType().GetProperties();
                foreach (PropertyInfo pi in propertys)
                {
                    string tempName = pi.Name;
                    if (dt.Columns.Contains(tempName))
                    {
                        if (!pi.CanWrite)
                            continue;
                        object value = dr[tempName];
                        if (value != DBNull.Value)
                        {
                            Type t = Nullable.GetUnderlyingType(pi.PropertyType) ?? pi.PropertyType;
                            object safeValue = (value == null) ? null : Convert.ChangeType(value, t);
                            pi.SetValue(entity, safeValue, null);
                        }
                    }
                }
                entityList.Add(entity);
            }
            return entityList;
        }
    }
    public sealed class ModelConverter
    {
        private enum ModelType
        {
            Struct,
            Enum,
            String,
            Object,
            Else
        }
        private static ModelType GetModelType(Type modelType)
        {
            if (modelType.IsEnum)//值类型
            {
                return ModelType.Enum;
            }
            if (modelType.IsValueType)//值类型
            {
                return ModelType.Struct;
            }
            else if (modelType == typeof(string))//引用类型 特殊类型处理
            {
                return ModelType.String;
            }
            else if (modelType == typeof(object))//引用类型 特殊类型处理
            {
                return ModelType.Object;
            }
            else//引用类型
            {
                return ModelType.Else;
            }
        }
        public static List<T> ToList<T>(DataTable table)
        {
            List<T> list = new List<T>();
            foreach (DataRow item in table.Rows)
            {
                list.Add(ToModel<T>(item));
            }
            return list;
        }
        public static T ToModel<T>(DataRow row)
        {
            T model;
            Type type = typeof(T);
            ModelType modelType = GetModelType(type);
            switch (modelType)
            {
                case ModelType.Struct://值类型
                    {
                        model = default(T);
                        if (row[0] != null)
                            model = (T)row[0];
                    }
                    break;
                case ModelType.Enum://值类型
                    {
                        model = default(T);
                        if (row[0] != null)
                        {
                            Type fiType = row[0].GetType();
                            if (fiType == typeof(int))
                            {
                                model = (T)row[0];
                            }
                            else if (fiType == typeof(string))
                            {
                                model = (T)Enum.Parse(typeof(T), row[0].ToString());
                            }
                        }
                    }
                    break;
                case ModelType.String://引用类型 c#对string也当做值类型处理
                    {
                        model = default(T);
                        if (row[0] != null)
                            model = (T)row[0];
                    }
                    break;
                case ModelType.Object://引用类型 直接返回第一行第一列的值
                    {
                        model = default(T);
                        if (row[0] != null)
                            model = (T)row[0];
                    }
                    break;
                case ModelType.Else://引用类型
                    {
                        model = Activator.CreateInstance<T>();//引用类型 必须对泛型实例化

                        #region 属性与DataTable处理

                        //获取model中的属性
                        PropertyInfo[] modelPropertyInfos = type.GetProperties();
                        //遍历model每一个属性并赋值DataRow对应的列
                        foreach (PropertyInfo pi in modelPropertyInfos)
                        {
                            //获取属性名称
                            String name = pi.Name;
                            if (row.Table.Columns.Contains(name) && row[name] != null)
                            {
                                ModelType piType = GetModelType(pi.PropertyType);
                                switch (piType)
                                {
                                    case ModelType.Struct:
                                        {
                                            var value = ChangeType(row[name], pi.PropertyType);//Convert.ChangeType(row[name], pi.PropertyType);
                                            pi.SetValue(model, value, null);
                                        }
                                        break;
                                    case ModelType.Enum:
                                        {
                                            Type fiType = row[0].GetType();
                                            if (fiType == typeof(int))
                                            {
                                                pi.SetValue(model, row[name], null);
                                            }
                                            else if (fiType == typeof(string))
                                            {
                                                var value = (T)Enum.Parse(typeof(T), row[name].ToString());
                                                if (value != null)
                                                    pi.SetValue(model, value, null);
                                            }
                                        }
                                        break;
                                    case ModelType.String:
                                        {
                                            var value = Convert.ChangeType(row[name], pi.PropertyType);
                                            pi.SetValue(model, value, null);
                                        }
                                        break;
                                    case ModelType.Object:
                                        {
                                            pi.SetValue(model, row[name], null);
                                        }
                                        break;
                                    case ModelType.Else:
                                        throw new Exception("不支持该类型转换");
                                    default:
                                        throw new Exception("未知类型");
                                }
                            }
                        }

                        #endregion
                    }
                    break;
                default:
                    model = default(T);
                    break;
            }
            return model;
        }
        static T ChangeType<T>(object value)
        {
            var t = typeof(T);

            if (t.IsGenericType && t.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
            {
                if (value == null)
                {
                    return default(T);
                }

                t = Nullable.GetUnderlyingType(t);
            }

            return (T)Convert.ChangeType(value, t);
        }
        static object ChangeType(object value, Type type)
        {
            if (type.IsGenericType && type.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
            {
                return type.IsValueType ? Activator.CreateInstance(type) : null;
            }
            return Convert.ChangeType(value, type);
        }
    }
    public sealed class DataTableConvert<Model> where Model : class
    {
        public DataTable ToDataTable(IList<Model> list)
        {
            DataTable table = CreateTable();
            Type entityType = typeof(Model);
            PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(entityType);

            foreach (Model item in list)
            {
                DataRow row = table.NewRow();

                foreach (PropertyDescriptor prop in properties)
                {
                    if (table.Columns.Contains(prop.Name))
                    {
                        if (prop.PropertyType == typeof(string))
                        {
                            row[prop.Name] = prop.GetValue(item) ?? string.Empty;
                        }
                        else if (prop.PropertyType == typeof(DateTime))
                        {
                            DateTime? dt = (prop.GetValue(item) as DateTime?);
                            if (dt != System.DateTime.MinValue)
                            {
                                row[prop.Name] = dt;
                            }
                            else
                            {
                                row[prop.Name] = System.Data.SqlTypes.SqlDateTime.MinValue.Value;
                            }
                        }
                        else
                        {
                            row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
                        }
                    }
                }
                table.Rows.Add(row);
            }
            return table;
        }
        private DataTable CreateTable()
        {
            Type entityType = typeof(Model);
            DataTable table = new DataTable(entityType.Name);
            PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(entityType);
            foreach (PropertyDescriptor prop in properties)
            {
                Type t = Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType;
                table.Columns.Add(prop.Name, t);
            }
            return table;
        }
    }
}
